package com.sayyi.validate.valid;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import java.util.*;

/**
 * 验证组件。因为需要设置、移除缓存，所以只能使用一次。
 * @author xuchuang
 * date 2021/12/7 3:16 PM
 */
final class ValidateUtil<T> {

    ValidateUtil(List<T> datas,
                 Map<String, Map<Object, Object>> maps,
                 Validator validator) {
        this.datas = datas;
        this.maps = maps;
        this.validator = validator;
    }

    /** 初始化缓存 */
    private void initCache() {
        if (maps == null) {
            return;
        }
        maps.forEach(ValidateMapCache::set);
    }
    /** 释放缓存 */
    private void releaseCache() {
        if (maps == null) {
            return;
        }
        maps.keySet().forEach(ValidateMapCache::remove);
    }

    /** 验证完成后，需要清理掉对应的缓存信息 */
    private final Map<String, Map<Object, Object>> maps;
    /** 待验证数据 */
    private final List<T> datas;
    /** 验证组件 */
    private final Validator validator;

    /**
     * 包可见的验证方法，只能使用一次
     * @return
     */
    List<ValidateResult<T>> valid(Class<?>... groups) {
        // 后续使用的时候，如有必要，可以考虑做成动态代理的方式，在那里进行数据的设置、清理
        initCache();
        try {
            List<ValidateResult<T>> validateResults = new ArrayList<>();
            int index = -1;
            for (T data : datas) {
                index++;
                final Set<ConstraintViolation<Object>> validate = validator.validate(data, groups);
                if (validate == null || validate.isEmpty()) {
                    continue;
                }
                ValidateResult<T> validateResult = ValidateResult.<T>builder()
                        .index(index)
                        .data(data)
                        .errorMsg(buildErrorMsg(validate))
                        .build();
                validateResults.add(validateResult);
            }
            return validateResults;
        } finally {
            releaseCache();
        }
    }

    /**
     * 构造异常信息。使用 逗号 连接多个异常。后续可以根据需要，使用接口来提供更加灵活的异常信息格式化方法
     * @param validate
     * @return
     */
    private String buildErrorMsg(Set<ConstraintViolation<Object>> validate) {
        StringJoiner stringJoiner = new StringJoiner("，");
        for (ConstraintViolation<Object> violation : validate) {
            stringJoiner.add(violation.getMessage());
        }
        return stringJoiner.toString();
    }
}
